﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Linq;
using System.ComponentModel;

using LostLib.Win32;
using System.Windows.Forms;
using System.Diagnostics;

namespace LostLib.Windows
{
	public partial class ScreenWindow : IEquatable<ScreenWindow>
	{
		internal ScreenWindow(IntPtr handle)
		{
			mHandle = handle;
			Debug.Assert((mHandle == IntPtr.Zero) || Exists);

			WindowsAPI.GetWindowThreadProcessId(mHandle, out procID);
		}

		IntPtr mHandle;
		public IntPtr Handle
		{
			get
			{
				return mHandle;
			}
		}

		public static ScreenWindow GetConsoleWindow()
		{
			return new ScreenWindow(WindowsAPI.GetConsoleWindow());
		}

		public static readonly ScreenWindow NullWindow = new ScreenWindow(IntPtr.Zero);

		public static implicit operator ScreenWindow(Control control)
		{
			return new ScreenWindow(control.Handle);
		}

		public bool Exists
		{
			get
			{
				return WindowsAPI.IsWindow(mHandle);
			}
		}

		public ScreenWindow Parent
		{
			get
			{
				return new ScreenWindow(WindowsAPI.GetParent(mHandle));
			}
			set
			{
				var old = WindowsAPI.SetParent(mHandle, value.mHandle);
				if (old == IntPtr.Zero)
					WindowsAPI.HandleWin32Exception();
			}
		}

		private WindowStyle Style
		{
			get
			{
				var result = (WindowStyle)WindowsAPI.GetWindowLong(mHandle, WindowParam.Style);

				if (result == WindowStyle.Null)
					WindowsAPI.HandleWin32Exception();

				return result;
			}
			set
			{
				var result = (WindowStyle)WindowsAPI.SetWindowLong(mHandle, WindowParam.Style, (int)value);

				if (result == WindowStyle.Null)
					WindowsAPI.HandleWin32Exception();
			}
		}

		public bool IsVisible
		{
			get
			{
				return (WindowInfo.Style & WindowStyle.Visible) == WindowStyle.Visible;
			}
		}

		public FormBorderStyle BorderStyle
		{
			get
			{
				var style = Style;
				if ((style & WindowStyle.Border) == WindowStyle.Null)
					return FormBorderStyle.None;

				if ((style & WindowStyle.SizeBox) == WindowStyle.Null)
				{
					//fixed window
					if ((style & WindowStyle.Dialog) == WindowStyle.Dialog)
						return FormBorderStyle.FixedDialog;

					return FormBorderStyle.Fixed3D;
				} else
				{
					return FormBorderStyle.Sizable;
				}
			}
			set
			{
				var style = Style;
				switch (value)
				{
				case FormBorderStyle.None:
					style &= ~WindowStyle.Caption;
					style &= ~WindowStyle.ThickFrame;
					style &= ~WindowStyle.SystemMenu;
					break;
				case FormBorderStyle.Sizable:
					style |= WindowStyle.SizeBox;
					break;
#warning FixedSingle & Fixed3D - the same effect
				case FormBorderStyle.FixedSingle:
				case FormBorderStyle.Fixed3D:
					style &= ~WindowStyle.SizeBox;
					break;
				case FormBorderStyle.FixedDialog:
					style &= ~WindowStyle.SizeBox;
					style |= WindowStyle.Dialog;
					break;
				default:
					throw new NotImplementedException();
				}
				Style = style;
			}
		}

		public int ClientLeft
		{
			get
			{
				return WindowInfo.ClientRect.Left;
			}
		}
		public int ClientTop
		{
			get
			{
				return WindowInfo.ClientRect.Top;
			}
		}
		public int Left
		{
			get
			{
				return WindowInfo.WindowRect.Left;
			}
			set
			{
				SetPosition(value, Top);
			}
		}
		public void SetPosition(int x, int y)
		{
			WindowsAPI.SetWindowPos(mHandle, IntPtr.Zero, x, y, 0, 0,
					WindowPosFlags.NoActivate | WindowPosFlags.NoCopyBits |
					WindowPosFlags.NoOwnerZOrder | WindowPosFlags.NoRedraw |
					WindowPosFlags.NoResize | WindowPosFlags.NoSendChanging |
					WindowPosFlags.NoZOrder);
		}
		public int Top
		{
			get
			{
				return WindowInfo.WindowRect.Top;
			}
			set
			{
				SetPosition(Left, value);
			}
		}
		public int ClientWidth
		{
			get
			{
				var rect = WindowInfo.ClientRect;
				return rect.Right - rect.Left;
			}
		}
		public int ClientHeight
		{
			get
			{
				var rect = WindowInfo.ClientRect;
				return rect.Bottom - rect.Top;
			}
		}

		WindowInfo WindowInfo
		{
			get
			{
				WindowInfo result;
				if (!WindowsAPI.GetWindowInfo(mHandle, out result))
					throw new NotImplementedException("реакция на ошибку");

				return result;
			}
		}

		public string Text
		{
			get
			{
				var result = new StringBuilder(1024);
				int len = WindowsAPI.GetWindowText(mHandle, result, result.Capacity);

				if (len == 0)
					WindowsAPI.HandleWin32Exception();

				return result.ToString();
			}
		}
		public string ClassName
		{
			get
			{
				var result = new StringBuilder(1024);
				int len = WindowsAPI.GetClassName(mHandle, result, result.Capacity);

				if (len == 0)
					WindowsAPI.HandleWin32Exception();

				return result.ToString();
			}
		}

		public void Hide()
		{
			if (!WindowsAPI.ShowWindow(mHandle, ShowWindowEnum.Hide))
				WindowsAPI.HandleWin32Exception();
		}

		public void Show()
		{
			if (!WindowsAPI.ShowWindow(mHandle, ShowWindowEnum.Show))
				WindowsAPI.HandleWin32Exception();
		}

		public static ScreenWindow GetForegroundWindow()
		{
			return new ScreenWindow(WindowsAPI.GetForegroundWindow());
		}

		static readonly ScreenWindow mDesktop = new ScreenWindow(WindowsAPI.GetDesktopWindow());
		public static ScreenWindow Root
		{
			get
			{
				return mDesktop;
			}
		}

		int procID;
		public int ProcessID
		{
			get
			{
				return procID;
			}
		}

		public IEnumerable<ScreenWindow> GetChilds(bool recoursive)
		{
			return from window in WindowsAPI.GetChildWindows(mHandle)
				   where (recoursive || (WindowsAPI.GetParent(window) == mHandle)) && WindowsAPI.IsWindow(window)
				   select new ScreenWindow(window);
		}
		public override string ToString()
		{
			var text = Text;
			if (!String.IsNullOrEmpty(text))
				return text;
			var className = ClassName;
			if (!String.IsNullOrEmpty(className))
				return string.Format("{0}[{1}]", className, mHandle);
			else
				return string.Format("[{0}]", mHandle);
		}

		public bool Equals(ScreenWindow other)
		{
			if (other == null)
				return false;
			return mHandle == other.mHandle;
		}
		public override bool Equals(object obj)
		{
			var window = obj as ScreenWindow;
			if (window == null)
				return false;

			return this.mHandle == window.mHandle;
		}

		public override int GetHashCode()
		{
			return mHandle.GetHashCode();
		}
	}
}
